import { ApplicationRef, Component, ElementRef, Input, NgZone, OnInit } from '@angular/core';
import { DefaultService } from '../../api';
import { SockJsService } from '../sock-js.service';
import { UserService } from '../user.service';
import { Subject } from 'rxjs';

declare var h337: any;
declare var BMap:any;
@Component({
  selector: 'box-display',
  styles: [`
    video, img {
      width: 100%;
    }
  `],
  template: `
    <!--Chart-->
    <div *ngIf="baseType==='chart'" echarts [options]="chartOption" [ngStyle]="{height:chartHeight+'px'}"
         (chartInit)="onChartInit($event)"></div>
    <!--Media-->
    <div *ngIf="baseType==='media'">
      <img *ngIf="mediaOption.isImage" [src]="mediaOption.src" [height]="mediaOption.height" (error)="mediaOption.isImage=false">
      <video *ngIf="mediaOption.isImage==false" [height]="mediaOption.height" loop autoplay controls>
        <source [src]="mediaOption.src" type="video/mp4">
      </video>
    </div>
    <div *ngIf="baseType==='map'" style="height:500px" [id]="'map-container-'+name"></div>
    <div *ngIf="baseType==='heatmap'" style="height:270px;width: 360px" [id]="'heatmap-'+name"></div>
  `
})
export class DisplayBox implements OnInit {
  @Input() name: string;
  @Input() baseType: string;
  @Input() toLoad: boolean;
  @Input() chartHeight: number;
  map:any;
  private loading: boolean;
  chartOption: ChartOption = null;
  echartInstance: any = null;
  data: { legend: string, data: any[][], type: string }[] = [];
  dataSources: { legend: string, device: string, sensor: number, offset: number, limit: number, type: string }[] = [];
  sockJsSources: { legend: string, device: string, sensor: number, type: string }[] = [];
  mediaOption: { height: number, src: string, isImage: boolean } = {height: 0, src: null, isImage: true};
  private _echartInstanceEvent = new Subject<any>();
  private echartInstanceEvent = this._echartInstanceEvent.asObservable();

  constructor(private api: DefaultService, private sockJs: SockJsService, private user: UserService, private app: ApplicationRef) {

  }

  ngOnInit(): void {
    console.log('ChartHeight:' + this.chartHeight);
    if (this.toLoad) {
      this.load();
    } else {
      switch (this.baseType) {
        case 'chart':
          this.chartOption = new ChartOption(this.name);
          break;
        case 'media':
          break;
      }
    }
  }

  setMedia(height: number, src: string) {
    this.mediaOption.height = height;
    this.mediaOption.src = src;
    this.save();
  }

  addData(legend: string, data: any[][], type: string = 'line') {
    switch (this.baseType) {
      case 'chart':
        if (this.echartInstance == null) {
          this.echartInstanceEvent.subscribe(() => {
            this.addData(legend, data, type);
          });
          return;
        }
        this.data.push({legend: legend, data: data, type: type});
        if (!this.loading) this.save();
        this.chartOption.legend.data.push(legend);
        this.chartOption.series.push({name: legend, type: type, data: data});
        this.echartInstance.setOption(this.chartOption);
        break;
    }
  }

  addDataSource(legend: string, device: string, sensor: number, offset: number = 0, limit: number = 20, type: string = 'line') {
    if (type == 'heatmap') {
      const heatmapInstance = h337.create({
        container: document.querySelector('#heatmap-' + this.name)
      });
      const points = [];
      this.dataSources.push({legend: legend, device: device, sensor: sensor, offset: offset, limit: limit, type: type});
      if (!this.loading) this.save();
      this.api.listData(device, sensor, offset, limit).subscribe(raw => {
        console.log(raw);
        let heat = new Uint8Array();
        while (true) {
          const data = raw.shift().data;
          console.log(data);
          let tmp = DisplayBox._base64ToArray(data);
          heat = DisplayBox._concatenate(tmp,heat);
          if(data.startsWith("WloCB")){
            break;
          }
        }
        console.log(heat);
        heat = heat.slice(4, heat.byteLength - 6);
        const real = [];
        for (let i = 0; i < heat.length; i += 2) {
          real.push((heat[i + 1] * 256 + heat[i]) / 100);
        }
        for (let i = 0; i < 24; i++) {
          for (let j = 0; j < 32; j++) {
            points.push({x: j * 360 / 32, y: i * 270 / 24, value: real[i * 32 + j]});
          }
        }
        console.log(real);
        let _data = {
          min: Math.min.apply(Math, real),
          max: Math.max.apply(Math, real),
          data: points
        };
        heatmapInstance.setData(_data);
      });
    } else if (type == 'map') {
      this.map = new BMap.Map("map-container-"+this.name);
      this.map.addControl(new BMap.NavigationControl());
      this.map.addControl(new BMap.ScaleControl());
      this.map.addControl(new BMap.OverviewMapControl());
      this.map.addControl(new BMap.MapTypeControl());
      this.map.enableScrollWheelZoom();
      this.api.listData(device, sensor, offset, limit).subscribe(raw => {
        const _point = raw.map(v => {
          const p = JSON.parse(v.data);
          const du1 = parseInt(String(p[0] / 100));
          const du2 = parseInt(String(p[1] / 100));
          return new BMap.Point(du2+(p[1]-du2*100)/60,du1+(p[0]-du1*100)/60);
        });
        console.log(_point);
        _point.forEach(point=>{
          let marker = new BMap.Marker(point);
          this.map.addOverlay(marker);
          this.map.centerAndZoom(point, 15);
        });
      });
    } else {
      if (this.echartInstance == null) {
        this.echartInstanceEvent.subscribe(() => {
          this.addDataSource(legend, device, sensor, offset, limit, type);
        });
        return;
      }
      this.dataSources.push({legend: legend, device: device, sensor: sensor, offset: offset, limit: limit, type: type});
      if (!this.loading) this.save();
      this.api.listData(device, sensor, offset, limit).subscribe(raw => {
        const data = raw.map(v => {
          return [new Date(v.updateTime), v.data];
        });
        let indexOfSeries = this.chartOption.legend.data.findIndex((v) => {
          return v == legend;
        });
        if (indexOfSeries == -1) {
          this.chartOption.legend.data.push(legend);
          this.chartOption.series.push({name: legend, type: type, data: data});
        } else {
          data.forEach(d => {
            this.chartOption.series[indexOfSeries].data.unshift(d);
          });
        }
        this.echartInstance.setOption(this.chartOption);
      });
    }
  }

  addSockJsSource(legend: string, device: string, sensor: number, type: string = 'line') {
    switch (this.baseType) {
      case 'chart':
        if (this.echartInstance == null) {
          this.echartInstanceEvent.subscribe(() => {
            this.addSockJsSource(legend, device, sensor, type);
          });
          return;
        }
        this.sockJsSources.push({legend: legend, device: device, sensor: sensor, type: type});
        if (!this.loading) this.save();
        let indexOfSeries = this.chartOption.legend.data.findIndex((v) => {
          return v == legend;
        });
        if (indexOfSeries == -1) {
          this.chartOption.legend.data.push(legend);
          indexOfSeries = this.chartOption.series.push({name: legend, type: type, data: []}) - 1;
        }
        this.sockJs.message.subscribe(value => {
          if (value.action !== 'data' || value.device.id !== device) return;
          const data = [new Date(), value.data[sensor.toString()]];
          const series = this.chartOption.series[indexOfSeries];
          series.data.pop();
          series.data.unshift(data);
          this.echartInstance.setOption(this.chartOption);
        });
        break;
    }
  }

  onChartInit(ec) {
    this.echartInstance = ec;
    this._echartInstanceEvent.next(ec);
  }

  private load() {
    this.loading = true;
    const object = JSON.parse(localStorage.getItem(`${this.user.me.username}-display-${this.name}`));
    console.log(`Load:${this.user.me.username},${this.name}`);
    this.baseType = object.baseType;
    //确保页面重新渲染
    setTimeout(()=>{
      switch (this.baseType) {
        case 'chart':
          this.chartOption = new ChartOption(name);
          break;
      }
      const data = object.data;
      data.forEach(value => {
        this.addData(value.legend, value.data, value.type);
      });
      const dataSources = object.dataSources;
      dataSources.forEach(value => {
        this.addDataSource(value.legend, value.device, value.sensor, value.offset, value.limit, value.type);
      });
      const sockJsSources = object.sockJsSources;
      sockJsSources.forEach(value => {
        this.addSockJsSource(value.legend, value.device, value.sensor, value.type);
      });
      this.mediaOption = object.mediaOption;
      this.loading = false;
    },500);

  }

  private save() {
    const object = {
      baseType: this.baseType
      , data: this.data
      , dataSources: this.dataSources
      , sockJsSources: this.sockJsSources
      , mediaOption: this.mediaOption
    };
    localStorage.setItem(`${this.user.me.username}-display-${this.name}`, JSON.stringify(object));
  }

  remove() {
    console.log(`remove:${this.name}`);
    localStorage.removeItem(`${this.user.me.username}-display-${this.name}`);
  }

  private static _base64ToArray(base64) {
    let binary_string = window.atob(base64);
    let len = binary_string.length;
    let bytes = new Uint8Array(len);
    for (let i = 0; i < len; i++) {
      bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes;
  }

  private static _concatenate(array1: Uint8Array, array2: Uint8Array) {
    let totalLength = array1.byteLength + array2.byteLength;
    let result = new Uint8Array(totalLength);
    let offset = 0;
    result.set(array1, offset);
    offset += array1.length;
    result.set(array2, offset);
    return result;
  }
}

class ChartOption {
  public title: { text: string };
  public tooltip: any = {
    trigger: 'axis',
    axisPointer: {
      type: 'cross',
      label: {
        backgroundColor: '#283b56'
      }
    }
  };
  public color: string[] = ['#59a1f8', '#78c87d', '#f6d464', '#445285', '#8e67de', '#e36f7e', '#70c9ca', '#d396c6', '#b09e6c', '#4f58d5', '#96a36f'];
  public legend = {x: 'right', y: 'top', data: []};
  public grid = {
    left: '1%',
    right: '1%',
    bottom: '1%',
    containLabel: true
  };
  public xAxis = {
    type: 'time',
    axisLabel: {
      interval: 0,
      rotate: 40
    },
  };
  public yAxis = {};
  public series: { type: string, data: any[], name: string }[] = [];

  constructor(title: string) {
    this.title = {text: title};
  }
}
